local snow     = require("meet_your_mods.mhr").snow
local status   = require("meet_your_mods.status")
local shortcut = require("meet_your_mods.shortcut")
local routine  = require("meet_your_mods.routine_framework")
local setting  = require("meet_your_mods.setting")
local ui       = require("meet_your_mods.ui")

snow.QuestMapManager.MapNoType:fetch_enum("none", "None")
snow.QuestMapManager.MapNoType:fetch_enum("default", "None")
snow.QuestMapManager.MapNoType:fetch_enum("special", "Max")
snow.QuestMapManager.MapNoType:fetch_enum("infernal_springs", "No09")
snow.QuestMapManager.MapNoType:fetch_enum("arena", "No10")
snow.QuestMapManager.MapNoType:fetch_enum("coral_palace", "No11")
snow.QuestMapManager.MapNoType:fetch_enum("forlorn_arena", "No41")

local quest = {
    setting = {
        show_all_quest = true,
        field          = snow.QuestMapManager.MapNoType.default,
    },

    except  = {
        quest   = { 315591, },
        monster = {
            snow.enemy.EnemyDef.EmTypes.EmType096_00, --风神龙
            snow.enemy.EnemyDef.EmTypes.EmType099_00, --雷神龙
            snow.enemy.EnemyDef.EmTypes.EmType099_05, --百龙渊源雷神龙
            snow.enemy.EnemyDef.EmTypes.EmType135_00, --冥渊龙
        },
    },
    quests  = nil,
    restore = nil,

    config        = nil,
    random_quests = nil,
    random_quest  = nil,

    page = {
        index = 1,
        max   = 1,
        size  = 20,
    },
    filter = {
        { enabled = false, invalid = 1, value = 1, }, --按照目标筛选
        { enabled = false, invalid = 1, value = 1, }, --按照地图筛选
        { enabled = false, invalid = 0, value = 0, }, --按照最小等级筛选
        { enabled = false, invalid = 0, value = 0, }, --按照最大等级筛选
    },

    window_opening        = false,
    quest_counter_opening = false,
}

function quest:initialize()
    setting:bind_setting("quest", self.setting)
    routine:hook(self)

    re.on_script_reset(function() self:update_quests(snow.QuestMapManager.MapNoType.default) end)

    snow.QuestManager.start:sdk_auto_hook(self)
    snow.QuestManager.setQuestClear:sdk_auto_hook(self)
    snow.progress.quest.ProgressQuestManager.isDisp:sdk_hook(function(_)
        return self.setting.show_all_quest and sdk.PreHookResult.SKIP_ORIGINAL or sdk.PreHookResult.CALL_ORIGINAL
    end, function(result)
        return self.setting.show_all_quest and sdk.to_ptr(1) or result
    end)

    --检测任务窗口
    snow.gui.fsm.questcounter.GuiQuestCounterFsmManager.awake:sdk_hook(nil,
        function(_) self.quest_counter_opening = true end)
    snow.gui.fsm.questcounter.GuiQuestCounterFsmManager.onDestroy:sdk_hook(nil,
        function(_)
            self.random_quests         = nil
            self.random_quest          = nil
            self.quest_counter_opening = false
        end)
    self.quest_counter_opening = snow.gui.fsm.questcounter.GuiQuestCounterFsmManager:get_singleton(true) ~= nil

    if status:current_status() ~= status.logical_status.none then
        self:post_before_village()
    end

    ui:cluster("easy_quest", "任务便利功能")
    ui:checkbox("easy_quest", "quest", "show_all_quest", "显示所有任务")

    ui:cluster("fatal_quest", "任务关键功能")
    ui:static_text("fatal_quest", "变更战斗地图：")
    ui:sameline("fatal_quest")
    ui:combo("fatal_quest", "quest", "field", nil, {
        snow.QuestMapManager.MapNoType.default,
        snow.QuestMapManager.MapNoType.special,
        snow.QuestMapManager.MapNoType.arena,
        snow.QuestMapManager.MapNoType.infernal_springs,
        snow.QuestMapManager.MapNoType.forlorn_arena,
    }, { "默认", "原图仅目标怪", "斗技场", "狱泉乡", "塔之秘境", },
        function() self:update_quests() end)

    ui:cluster("inventory", "存档功能")
    ui:button("inventory", "编辑怪异调查任务", self.toggle_window, self)

    ui:child_window(function() return self.window_opening end, function() self.window_opening = false end,
        "怪异调查任务", self.draw_window, self)
end

function quest:normalize()
    self:update_quests()
end

function quest:post_before_village(_)
    self:load_config()

    if self.restore then
        self:update_quest(self.restore, self.setting.field)
        self.restore = nil
    end
end

function quest:pre_return_title(_)
    self.quest_counter_opening = false
    self.random_quests         = nil
    self.random_quest          = nil
end

function quest:load_config()
    if self.config then return end

    local config   = self:generate_static_config()
    config.monster = self:load_monster_config()
    config.map     = self:load_map_config(config.monster.map)
    self.config    = config
end

function quest:generate_static_config()
    local uuid = ui:generate_uuid()

    local config = {
        hunter = { uuid = uuid .. "/hunter", value = { 2, 4, }, ui = {}, hash = {}, },
        faint  = { uuid = uuid .. "/faint", value = { 1, 2, 3, 4, 5, 9, }, ui = {}, hash = {}, },
        target = { uuid = uuid .. "/target", value = { 1, 2, 3, }, ui = {}, hash = {}, },
        time   = { uuid = uuid .. "/time", value = { 25, 30, 35, 50, }, ui = {}, hash = {}, },
    }

    for _, config in pairs(config) do
        for index, value in pairs(config.value) do
            config.ui[index]   = tostring(value)
            config.hash[value] = index
        end
    end

    config.day = {
        uuid  = uuid .. "/day",
        value = {
            snow.quest.StartTimeType.Default,
            snow.quest.StartTimeType.Day,
            snow.quest.StartTimeType.Night,
        },
        ui    = { "默认", "昼", "夜", },
        hash  = {
            [snow.quest.StartTimeType.Default] = 1,
            [snow.quest.StartTimeType.Day]     = 2,
            [snow.quest.StartTimeType.Night]   = 3,
        },
    }

    config.filter = {
        uuid .. "/filter/monster",
        uuid .. "/filter/map",
        uuid .. "/filter/min-level",
        uuid .. "/filter/max-level",
    }
    config.level = {
        uuid = uuid .. "/level",
        max  = 0,
        top  = snow.QuestManager.MaxRandomMysteryQuestLv._typedef_:get_data(nil),
    }

    config.status = {
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.OK]               = "合法",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_INVALID_QUEST] = "非法",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_QUESTLEVEL]    = "任务等级过高",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_MONSTERRANK]   = "怪物级别过高",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_MONSTER]       = "非法怪物",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_LOWERLIMIT]    = "任务等级过低",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_QUESTCOND]     = "非法任务条件",
        [snow.quest.nRandomMysteryQuest.QuestCheckResult.Max + 1]          = "无法生成更多怪异调查任务",
    }

    config.quest = {
        max_quantity = snow.quest.nRandomMysteryQuest.RandomMysteryQuestMax._typedef_:get_data(nil),
        id_base      = snow.quest.QuestNo.QN_RondomMystery,
    }

    return config
end

function quest:load_monster_config()
    local uuid   = ui:generate_uuid()
    local config = {
        rank    = {},
        capture = {},
        map     = {},
        all     = {
            ui    = { "------", },
            value = { snow.enemy.EnemyDef.EmTypes.EmTypeNoData, },
            hash  = { [snow.enemy.EnemyDef.EmTypes.EmTypeNoData] = 1, },
        },
        uuid    = {
            uuid .. "/1",
            uuid .. "/2",
            uuid .. "/3",
            uuid .. "/4",
        },
    }

    for _, enemy in pairs(snow.QuestManager:get_singleton()._randomMysteryLotEnemyData._LotEnemyList) do
        if not enemy:isValid() then
            goto next_monster
        end

        local monster = enemy:get_EmType()
        local rank    = enemy:get_MysteryRank()
        local caputre = shortcut:is_enable_capture(monster)
        local name    = shortcut:get_enemy_name(enemy:get_EmType())
        local valid   = {
            enemy:get_IsMystery(),
            enemy:get_IsEnableSub(),
            enemy:get_IsIntrusion(),
        }

        if valid[1] then
            table.insert(config.all.ui, name)
            table.insert(config.all.value, monster)
            config.all.hash[monster] = #config.all.ui
        end

        for _, map in pairs(enemy:getStageLotTable(true)) do
            local monsters = config.map[map]
            if not monsters then
                monsters = {
                    ui    = { { "------", }, { "------", }, { "------", }, },
                    value = { { 0, }, { 0, }, { 0, }, },
                    hash  = { { [0] = 1, }, { [0] = 1, }, { [0] = 1, }, },
                }
                config.map[map] = monsters
            end

            for index, is_valid in pairs(valid) do
                if not is_valid then
                    goto next_monster_type
                end

                table.insert(monsters.ui[index], name)
                table.insert(monsters.value[index], monster)
                monsters.hash[index][monster] = #monsters.ui[index]

                config.rank[monster]    = rank
                config.capture[monster] = caputre
                ::next_monster_type::
            end
        end
        ::next_monster::
    end

    for _, monsters in pairs(config.map) do
        for _, content in pairs(monsters) do
            table.insert(content, 3, content[2])
        end
    end

    return config
end

function quest:load_map_config(avaliable_maps)
    local config = {
        uuid  = ui:generate_uuid(avaliable_maps),
        value = { snow.QuestMapManager.MapNoType.none, },
        ui    = { "------", },
        hash  = { [snow.QuestMapManager.MapNoType.none] = 1, },
    }
    for map, _ in pairs(avaliable_maps) do
        table.insert(config.value, map)
    end

    table.sort(config.value, function(left, right) return left < right end)
    for index, map in pairs(config.value) do
        table.insert(config.ui, shortcut:get_map_name(map))
        config.hash[map] = index
    end

    return config
end

function quest:load_random_quests()
    if self.random_quests or self.quest_counter_opening
    then
        return
    end

    if not self.config then
        self:pre_return_title()
        return
    end

    local manager = snow.QuestManager:get_singleton()
    self.config.level.max = manager:getMysteryResearchLevel()

    local quests = { all = {}, filtered = {}, }
    local uuid   = ui:generate_uuid(quests)
    for index, quest in pairs(manager:get_RandomMysteryQuestDataArray()) do
        if quest._QuestNo ~= -1 then
            quest      = self:load_random_quest(nil, quest)
            quest.uuid = uuid .. "/" .. index
            table.insert(quests.all, quest)
        end
    end
    table.sort(quests.all, function(left, right) return left._Idx > right._Idx end)

    quests.filtered = self:filter_random_quests(quests.all)
    self.page.max   = math.max(1, math.ceil(#quests.filtered / self.page.size))
    self.page.index = 1

    self.random_quests = quests
end

function quest:load_random_quest(quest, instance)
    quest    = quest or {}
    instance = quest and quest.instance or instance

    quest.status      = snow.quest.nRandomMysteryQuest:checkRandomMysteryQuestOrderBan(instance:get_instance(), false)
    quest.instance    = instance
    quest._BossEmType = {}

    for _, key in pairs({ "_Idx", "_QuestLv", "_OriginQuestLv", "_MapNo", "_StartTime", "_TimeLimit", "_QuestLife",
        "_QuestOrderNum", "_HuntTargetNum",
    }) do
        quest[key] = instance[key]
    end
    quest.level = 0 == quest._OriginQuestLv and quest._QuestLv or quest._OriginQuestLv

    local monster = instance._BossEmType
    for _, index in pairs({ 1, 2, 3, 6, }) do
        table.insert(quest._BossEmType, monster[index])
    end

    return quest
end

function quest:filter_random_quests(quests)
    local filter_enabled = false
    for _, filter in pairs(self.filter) do
        if filter.enabled then
            filter_enabled = true
            break
        end
    end

    local result = {}
    for _, quest in pairs(quests) do
        if not filter_enabled or self:check_random_quest_filter(quest) then
            table.insert(result, quest)
        end
    end

    return result
end

function quest:check_random_quest_filter(quest)
    local checklist = {
        function(data) return quest._BossEmType[1] == self.config.monster.all.value[data] end,
        function(data) return quest._MapNo == self.config.map.value[data] end,
        function(data) return quest.level >= data end,
        function(data) return quest.level <= data end,
    }

    for index, filter in pairs(self.filter) do
        if filter.enabled and filter.value ~= filter.invalid
            and not checklist[index](filter.value)
        then
            return false
        end
    end

    return true
end

function quest:toggle_window()
    self.window_opening = not self.window_opening
end

function quest:draw_window()
    imgui.spacing()
    imgui.indent(4)

    if self.quest_counter_opening then
        imgui.text("关闭任务窗口以编辑怪异调查任务")
        return
    end
    self:load_random_quests()

    if not self.random_quest then
        self:draw_browser()
    else
        self:draw_editor(self.random_quest)
    end
end

function quest:draw_browser()
    self:draw_filter()

    imgui.spacing()
    if imgui.button("＜") then
        self.page.index = self.page.index - 1 >= 1 and self.page.index - 1 or self.page.max
    end

    imgui.same_line()
    imgui.text(string.format("%d/%d", self.page.index, self.page.max))
    imgui.same_line()
    if imgui.button("＞") then
        self.page.index = self.page.index + 1 <= self.page.max and self.page.index + 1 or 1
    end

    imgui.same_line()
    imgui.text(string.format("任务数量/最大数量：%d/%d", #self.random_quests.all, self.config.quest.max_quantity))

    imgui.same_line()
    if imgui.button("生成新任务") then
        self:generate_random_quest()
    end

    if #self.random_quests.filtered <= 0 then
        imgui.text("无怪异化任务")
        return
    end

    if not imgui.begin_table(nil, 8, imgui.TableFlags.NoSavedSettings | imgui.TableFlags.RowBg) then
        return
    end

    imgui.table_setup_column("等级", imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column("首要目标", imgui.ColumnFlags.WidthStretch)
    imgui.table_setup_column("战斗区域", imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column("额外目标", imgui.ColumnFlags.WidthStretch)
    imgui.table_setup_column(nil, imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column(nil, imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column(nil, imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column(nil, imgui.ColumnFlags.WidthFixed)
    imgui.table_headers_row()

    for index = 1 + (self.page.index - 1) * self.page.size, math.min(self.page.index * self.page.size,
        #self.random_quests.filtered) do
        local quest    = self.random_quests.filtered[index]
        local monsters = self.config.monster.map[quest._MapNo]

        imgui.table_next_column()
        imgui.text(quest.level)

        if snow.quest.nRandomMysteryQuest.QuestCheckResult.OK == quest.status then
            imgui.table_next_column()
            imgui.text(monsters.ui[1][ monsters.hash[1][quest._BossEmType[1]] ])

            imgui.table_next_column()
            imgui.text(self.config.map.ui[self.config.map.hash[quest._MapNo]])

            imgui.table_next_column()
            if 1 == quest._HuntTargetNum then
                imgui.text()
            else
                for index = 2, quest._HuntTargetNum do
                    imgui.text(monsters.ui[index][ monsters.hash[index][quest._BossEmType[index]] ])
                end
            end
        else
            imgui.table_next_column()
            imgui.text(self.config.monster.all.ui[ self.config.monster.all.hash[quest._BossEmType[1]] ])

            imgui.table_next_column()
            imgui.text("任务非法")

            imgui.table_next_column()
            imgui.text_colored(self.config.status[quest.status], 0xFF0000FF)
        end

        imgui.table_next_column()
        imgui.text(quest._TimeLimit .. "分")

        imgui.table_next_column()
        imgui.text(quest._QuestOrderNum .. "人")

        imgui.table_next_column()
        imgui.text(quest._QuestLife .. "猫")

        imgui.table_next_column()
        imgui.push_id(quest.uuid)
        if imgui.button("编辑") then
            self:edit_random_quest(quest)
        end
        imgui.pop_id()
    end

    imgui.end_table()
end

function quest:draw_filter()
    local changed, value = false, nil
    local has_changed    = false

    local filters = {
        {
            label = "首要目标",
            draw = function() return imgui.combo(nil, self.filter[1].value, self.config.monster.all.ui) end,
        },
        {
            label = "战斗区域",
            draw  = function() return imgui.combo(nil, self.filter[2].value, self.config.map.ui) end,
        },
        {
            label = "最低等级",
            draw  = function()
                return imgui.slider_int(nil, self.filter[3].value, 0, self.config.level.max, "Lv.%d")
            end,
            post  = function(value)
                return self.filter[4].enabled and self.filter[4].value ~= self.filter[4].invalid
                    and value > self.filter[4].value and self.filter[4].value or value
            end,
        },
        {
            label = "最高等级",
            draw  = function()
                return imgui.slider_int(nil, self.filter[4].value, 0, self.config.level.max, "Lv.%d")
            end,
            post  = function(value)
                return self.filter[3].enabled and self.filter[3].value ~= self.filter[3].invalid
                    and value < self.filter[3].value and self.filter[3].value or value
            end,
        },
    }

    if not imgui.tree_node("筛选任务") then return end
    for index, filter in pairs(filters) do
        changed, self.filter[index].enabled = imgui.checkbox(filter.label, self.filter[index].enabled)
        if changed then has_changed = true end

        if self.filter[index].enabled then
            imgui.same_line()
            imgui.set_next_item_width(-1)
            imgui.push_id(self.config.filter[index])
            changed, value = filter.draw()
            imgui.pop_id()

            if changed then
                value = filter.post and filter.post(value) or value
                if value ~= self.filter[index].value then
                    self.filter[index].value = value
                    has_changed = true
                end
            end
        end
    end
    imgui.tree_pop()

    if has_changed then self:apply_quest_filter() end
end

function quest:apply_quest_filter()
    self.random_quests.filtered = self:filter_random_quests(self.random_quests.all)
    self.page.max               = math.max(1, math.ceil(#self.random_quests.filtered / self.page.size))
    self.page.index             = 1
end

function quest:edit_random_quest(quest)
    quest.changed = false

    --修正任务等级
    if quest.level < 1 or quest.level > self.config.level.max then
        quest.level   = self.config.level.max
        quest.changed = true
    end

    for _, check_list in pairs({
        { key = "_MapNo", config = self.config.map, },
        { key = "_StartTime", config = self.config.day, },
        { key = "_TimeLimit", config = self.config.time, },
        { key = "_QuestLife", config = self.config.faint, },
        { key = "_QuestOrderNum", config = self.config.hunter, },
    }) do
        if not check_list.config.hash[quest[check_list.key]] then
            quest[check_list.key] = check_list.config.value[1]
            quest.changed = true
        end
    end

    local monsters = self.config.monster.map[quest._MapNo]
    if not monsters then
        for index, _ in pairs(quest._BossEmType) do
            quest._BossEmType[index] = snow.enemy.EnemyDef.EmTypes.EmTypeNoData
            quest.changed = true
        end
    else
        for index, monster in pairs(quest._BossEmType) do
            if not monsters.hash[index][monster] then
                quest._BossEmType[index] = snow.enemy.EnemyDef.EmTypes.EmTypeNoData
                quest.changed = true
            end
        end
    end

    quest.target = {}
    for index = 1, quest._HuntTargetNum do
        quest.target[index] = true
    end

    self.random_quest = quest
end

function quest:draw_editor(quest)
    if imgui.button("保存") then
        self:update_random_quest(quest)
    end

    imgui.same_line()
    if imgui.button("重置") then
        self:load_random_quest(quest)
        self:edit_random_quest(quest)
    end

    imgui.same_line()
    if imgui.button("返回") then
        self.random_quest = nil
        quest.changed     = nil
        quest.target      = nil
        self:load_random_quest(quest)
        self:apply_quest_filter()
        return
    end

    imgui.text()
    imgui.text("合法检测：")
    imgui.same_line()
    imgui.text_colored(self.config.status[quest.status],
        snow.quest.nRandomMysteryQuest.QuestCheckResult.OK == quest.status and 0xFFFFFFFF or 0xFF0000FF)

    imgui.text()
    self:draw_info_editor(quest)

    imgui.text()
    self:draw_monster_editor(quest)
end

function quest:draw_info_editor(quest)
    imgui.text("战斗区域：")
    imgui.same_line()
    imgui.set_next_item_width(-1)
    imgui.push_id(self.config.map.uuid)

    local changed, result = imgui.combo(nil, self.config.map.hash[quest._MapNo], self.config.map.ui)
    imgui.pop_id()

    if changed and result ~= self.config.map.hash[quest._MapNo] then
        self:update_random_quest_map(quest, self.config.map.value[result])
        quest.changed = true
    end

    local quest_info = {
        { label = "出发时刻：", data = "_StartTime", config = self.config.day, },
        { label = "任务时限：", data = "_TimeLimit", config = self.config.time, },
        { label = "参与人数：", data = "_QuestOrderNum", config = self.config.hunter, },
        { label = "猫车次数：", data = "_QuestLife", config = self.config.faint, },
    }
    for _, info in pairs(quest_info) do
        imgui.text(info.label)
        imgui.same_line()
        imgui.set_next_item_width(-1)
        imgui.push_id(info.config.uuid)

        changed, result = imgui.combo(nil, info.config.hash[quest[info.data]], info.config.ui)
        imgui.pop_id()

        if changed and result ~= info.config.hash[quest[info.data]] then
            quest[info.data] = info.config.value[result]
            quest.changed    = true
        end
    end

    imgui.text("任务等级：")
    imgui.same_line()
    imgui.set_next_item_width(-1)
    imgui.push_id(self.config.level.uuid)
    changed, quest.level = imgui.slider_int(nil, quest.level, 1, self.config.level.max)
    quest.changed = changed or quest.changed
    imgui.pop_id()
end

function quest:draw_monster_editor(quest)
    local monsters     = self.config.monster.map[quest._MapNo]
    local monster_info = {
        { label = "必选", },
        { label = "可选", set_target = true, },
        { label = "可选", set_target = true, },
        { label = "乱入", },
    }

    imgui.text("怪物信息：")
    if not imgui.begin_table(nil, 3, imgui.TableFlags.NoSavedSettings) then
        return
    end
    imgui.table_setup_column("目标", imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column("类别", imgui.ColumnFlags.WidthFixed)
    imgui.table_setup_column("怪物", imgui.ColumnFlags.WidthStretch)
    imgui.table_headers_row()

    local changed, result = false, nil
    for index, info in pairs(monster_info) do
        imgui.table_next_column()
        imgui.push_id(self.config.monster.uuid[index] .. "/target")
        if not info.set_target then
            imgui.checkbox(nil, quest.target[index])
        else
            result = select(2, imgui.checkbox(nil, quest.target[index]))
                and quest._BossEmType[index] ~= snow.enemy.EnemyDef.EmTypes.EmTypeNoData
            if result ~= quest.target[index] then
                quest.target[index] = result
                quest.changed = true
            end
        end
        imgui.pop_id()

        imgui.table_next_column()
        imgui.text(info.label)

        imgui.table_next_column()
        imgui.set_next_item_width(-1)
        if not monsters then
            imgui.text("需先选定合法战斗区域")
            goto next_monster
        end

        imgui.push_id(self.config.monster.uuid[index] .. "/monster")
        changed, result = imgui.combo(nil, monsters.hash[index][quest._BossEmType[index]], monsters.ui[index])
        imgui.pop_id()

        if changed and result ~= monsters.hash[index][quest._BossEmType[index]] then
            quest._BossEmType[index] = monsters.value[index][result]
            quest.changed            = true

            if info.set_target and snow.enemy.EnemyDef.EmTypes.EmTypeNoData == quest._BossEmType[index] then
                quest.target[index] = false
            end
        end
        ::next_monster::
    end
    imgui.end_table()
end

function quest:generate_random_quest()
    if #self.random_quests.all >= self.config.quest.max_quantity then
        return
    end

    local quest_manager    = snow.QuestManager:get_singleton()
    local free_quest_id    = quest_manager:getFreeMysteryQuestNo()
    local free_quest_index = quest_manager:getFreeMysteryQuestDataIdx2IndexList(
        quest_manager:getFreeSpaceMysteryQuestIDXList(quest_manager:get_RandomMysteryQuestDataArray():get_instance(),
            free_quest_id, 1, true):get_instance())[1]

    local quest   = snow.quest.RandomMysteryQuestData:new()
    local monster = quest._BossEmType

    quest._QuestLv = self.config.level.top
    monster[1] = snow.enemy.EnemyDef.EmTypes.EmType001_00
    monster[2] = snow.enemy.EnemyDef.EmTypes.EmType001_00
    monster[3] = snow.enemy.EnemyDef.EmTypes.EmType001_00

    snow.quest.nRandomMysteryQuest:CreateRandomMysteryQuest(
        quest:get_instance(),
        snow.quest.nRandomMysteryQuest.LotType.Random,
        free_quest_index,
        free_quest_id + self.config.quest.id_base,
        true)

    self.random_quests = nil
    self:load_random_quests()
end

function quest:update_random_quest_map(quest, map)
    local hash = self.config.monster.map[map].hash
    for index, monster in pairs(quest._BossEmType) do
        if not hash[index][monster] then
            quest._BossEmType[index] = snow.enemy.EnemyDef.EmTypes.EmTypeNoData
        end
    end
    quest._MapNo = map
end

function quest:update_random_quest(quest)
    if not quest.changed then
        return
    end

    if snow.enemy.EnemyDef.EmTypes.EmTypeNoData == quest._BossEmType[1] or not quest.target[1] then
        quest.status = snow.quest.nRandomMysteryQuest.QuestCheckResult.NG_INVALID_QUEST
        return
    end
    self:normalize_monster_data(quest)

    if 0 == quest._OriginQuestLv then
        quest._QuestLv = quest.level
    else
        quest._OriginQuestLv = quest.level
    end

    for _, key in pairs({
        "_QuestLv", "_OriginQuestLv", "_MapNo", "_StartTime", "_TimeLimit", "_QuestLife", "_QuestOrderNum",
        "_HuntTargetNum",
    }) do
        quest.instance[key] = quest[key]
    end

    quest.instance._IsNewFlag    = true
    quest._MainTargetMysteryRank = self.config.monster.rank[quest._BossEmType[1]]
    quest.instance._QuestType    = snow.quest.QuestType.KILL
    if 2 == quest._HuntTargetNum and self.config.monster.capture[quest._BossEmType[2]] or
        3 == quest._HuntTargetNum and
        (self.config.monster.capture[quest._BossEmType[2]] or self.config.monster.capture[quest._BossEmType[3]])
    then
        quest.instance._QuestType = snow.quest.QuestType.HUNTING
    end

    --设置怪物ID
    local monsters = quest.instance._BossEmType
    for index, _ in pairs(monsters) do
        monsters[index] = snow.enemy.EnemyDef.EmTypes.EmTypeNoData
    end
    for index, data_index in pairs({ 1, 2, 3, 6, }) do
        monsters[data_index] = quest._BossEmType[index]
    end

    --设置出生逻辑
    local conditions = quest.instance._BossSetCondition
    for index, _ in pairs(conditions) do
        conditions[index] = snow.QuestManager.BossSetCondition.NONE
    end
    for index, data_index in pairs({ 1, 2, 3, 6, }) do
        if quest._HuntTargetNum >= index then
            conditions[data_index] = snow.QuestManager.BossSetCondition.DEFAULT
        elseif quest._BossEmType[index] ~= snow.enemy.EnemyDef.EmTypes.EmTypeNoData then
            conditions[data_index] = snow.QuestManager.BossSetCondition.INIT_RANDOM
        end
    end
    if 3 == quest._HuntTargetNum then
        conditions[1] = snow.QuestManager.BossSetCondition.FREE_2
    end

    if quest._BossEmType[4] ~= snow.enemy.EnemyDef.EmTypes.EmTypeNoData then
        conditions[6] = snow.QuestManager.BossSetCondition.SWAP_RANDOM
        quest.instance._SwapSetCondition[1] = snow.QuestManager.SwapSetCondition.QuestTimer
        quest.instance._SwapSetParam[1]     = 12
        quest.instance._SwapStopType        = snow.QuestManager.SwapStopType.LowerHP
        quest.instance._SwapExecType        = snow.QuestManager.SwapExecType.FreeExtra
    else
        quest.instance._SwapSetCondition[1] = snow.QuestManager.SwapSetCondition.None
        quest.instance._SwapSetParam[1]     = 0
        quest.instance._SwapStopType        = snow.QuestManager.SwapStopType.None
        quest.instance._SwapExecType        = snow.QuestManager.SwapExecType.None
    end

    self:update_random_seed(quest)
    quest.status  = snow.quest.nRandomMysteryQuest:checkRandomMysteryQuestOrderBan(quest.instance:get_instance(), false)
    quest.changed = false
end

function quest:normalize_monster_data(quest)
    local monsters = { {}, {}, }
    for index = 1, 4 do
        if quest.target[index] then
            table.insert(monsters[1], quest._BossEmType[index])
        else
            table.insert(monsters[2], quest._BossEmType[index])
        end
    end

    local results = {}
    for _, monster in pairs(monsters[1]) do
        table.insert(results, monster)
    end
    for _, monster in pairs(monsters[2]) do
        table.insert(results, monster)
    end

    if 3 == #monsters[1] then
        results[4] = snow.enemy.EnemyDef.EmTypes.EmTypeNoData
    end

    quest._BossEmType = results
    quest._HuntTargetNum = #monsters[1]

    quest.target = {}
    for index = 1, quest._HuntTargetNum do
        quest.target[index] = true
    end
end

function quest:update_random_seed(quest)
    local quest_manager = snow.QuestManager:get_singleton()
    local mystery_seed  = quest_manager:get_SaveData()._RandomMysteryQuestSeed
    local random_seed   = quest_manager:getRandomQuestSeedFromQuestNo(quest.instance._QuestNo)
    local seed_index    = mystery_seed:IndexOf(random_seed:get_instance())

    if not random_seed or not seed_index then
        return
    end

    for _, key in pairs({
        "_QuestLv", "_OriginQuestLv", "_MapNo", "_StartTime", "_TimeLimit", "_QuestLife", "_QuestOrderNum",
        "_HuntTargetNum",
    }) do
        random_seed[key] = quest[key]
    end
    random_seed._QuestType = quest.instance._QuestType
    random_seed._MysteryLv = self.config.level.top
    random_seed:setEnemyTypes(quest.instance._BossEmType:get_instance())
    mystery_seed[seed_index + 1] = random_seed:get_instance()
end

function quest:post_start(_)
    self:update_quests()
end

function quest:pre_setQuestClear(_)
    if snow.QuestMapManager.MapNoType.default == self.setting.field then
        return sdk.PreHookResult.CALL_ORIGINAL
    end

    local quest_manager = snow.QuestManager:get_singleton()
    if quest_manager:isMysteryQuest() then
        self.restore = quest_manager:getQuestNo()
        self:update_quest(quest_manager:getQuestNo(), snow.QuestMapManager.MapNoType.default)
    end

    return sdk.PreHookResult.CALL_ORIGINAL
end

function quest:load_quests()
    if self.quests then
        return self.quests
    end

    local quest_manager = snow.QuestManager:get_singleton()
    if not quest_manager then
        return nil
    end

    local quests, dumped, except = {}, {}, { quest = {}, monster = {}, }
    for _, id in pairs(self.except.quest) do
        except.quest[id] = true
    end
    for _, id in pairs(self.except.monster) do
        except.monster[id] = true
    end
    self.except = except

    for _, index in pairs({
        "_normalQuestData", "_DlQuestData", "_DlQuestData_MR", "_nomalQuestDataKohaku",
    }) do
        for _, quest in pairs(quest_manager[index]._Param) do
            self:load_quest_data(quest, dumped, quests)
        end
    end

    for _, index in pairs({
        "_normalQuestDataForEnemy", "_DlQuestDataForEnemy", "_DlQuestDataForEnemy_MR", "_normalQuestDataForEnemyKohaku",
    }) do
        for _, quest in pairs(quest_manager[index]._Param) do
            self:load_quest_enemy(quest, dumped, quests)
        end
    end

    local template = {
        data = {
            _BossEmType       = { 0, 0, 0, 0, 0, 0, 0, },
            _BossSetCondition = { snow.QuestManager.BossSetCondition.DEFAULT, 0, 0, 0, 0, 0, 0, },
            _InitExtraEmNum   = 0,
            _MapNo            = snow.QuestMapManager.MapNoType.default,
            _SwapEmRate       = { 0, 0, },
            _SwapExitTime     = { 0, 0, },
            _SwapSetCondition = { snow.QuestManager.SwapSetCondition.None, snow.QuestManager.SwapSetCondition.None, },
            _SwapSetParam     = { 0, 0, },
        },
        enemy = {
            _EmsSetNo    = snow.quest.EmsSetNo.ems_set_none,
            _RouteNo     = { 10, },
            _InitSetName = { "", },
        },
    }

    self.quests = { instance = quests, dumped = dumped, template = template, }
    return self.quests
end

function quest:load_quest_data(quest, dumped, quests)
    local quest_id = quest._QuestNo
    if not quest_id or self.except.quest[quest_id] or self.except.monster[quest._TgtEmType[1]]
        or quest._QuestType ~= snow.quest.QuestType.HUNTING
        and quest._QuestType ~= snow.quest.QuestType.KILL
        and quest._QuestType ~= snow.quest.QuestType.CAPTURE
        or quest._TgtEmType[2] ~= snow.enemy.EnemyDef.EmTypes.EmTypeNoData
        or quest._TgtNum[1] ~= 1
    then
        return
    end

    local data = dumped[quest_id]
    if not data then
        data = { data = {}, enemy = {}, }
        dumped[quest_id] = data
        quests[quest_id] = { data = quest, }
    end
    data = data.data

    data._MapNo          = quest._MapNo
    data._InitExtraEmNum = quest._InitExtraEmNum

    for _, index in pairs({
        "_BossEmType", "_BossSetCondition", "_SwapEmRate",
        "_SwapSetCondition", "_SwapSetParam", "_SwapExitTime",
    }) do
        data[index] = {}
        for _, value in pairs(quest[index]) do
            table.insert(data[index], value)
        end
    end
end

function quest:load_quest_enemy(quest, dumped, quests)
    local quest_id = quest._QuestNo
    if not quest_id then
        return
    end

    local data = dumped[quest_id]
    if not data then
        return
    end
    quests[quest_id].enemy = quest
    data = data.enemy

    data._EmsSetNo    = quest._EmsSetNo
    data._RouteNo     = { quest._RouteNo[1], }
    data._InitSetName = { quest._InitSetName[1], }
end

function quest:update_quests(field)
    if not self:load_quests() then
        return
    end

    field = field or self.setting.field
    for quest_id, _ in pairs(self.quests.dumped) do
        self:update_quest(quest_id, field)
    end
end

function quest:update_quest(quest_id, field)
    local instance = self.quests.instance[quest_id]
    local result   = self.quests.dumped[quest_id]
    if not instance or not result then
        return
    end

    if snow.QuestMapManager.MapNoType.special == field then
        field = result.data._MapNo
    end

    if field ~= snow.QuestMapManager.MapNoType.default then
        result                       = self.quests.template
        result.data._MapNo           = field
        result.data._BossEmType[1]   = instance.data._BossEmType[1]
        result.enemy._InitSetName[1] = snow.QuestMapManager.MapNoType.arena == field and "A" or "メイン"
    end

    for instance_type, content in pairs(result) do
        for key, data in pairs(content) do
            if type(data) ~= "table" then
                instance[instance_type][key] = data
                goto next_key
            end

            for index, value in pairs(data) do
                instance[instance_type][key][index] = value
            end
            ::next_key::
        end
    end
end

return quest
